#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "userprog/pagedir.h"
#include "threads/vaddr.h"

#include <string.h>
#include "threads/vaddr.h"
#include "devices/shutdown.h"
#include "filesys/filesys.h"
#include "filesys/file.h"
#include "devices/input.h"
#include "lib/kernel/stdio.h"
#include "userprog/process.h"

#define MAX_CHAR_NUM 200

static void syscall_handler(struct intr_frame *);
static bool file_create_handler(char *, unsigned);
static int file_read_handler(int, void *, unsigned);
static struct file *get_file_through_fd(int fd);
void syscall_init(void)
{
  intr_register_int(0x30, 3, INTR_ON, syscall_handler, "syscall");
}

struct file *get_file_through_fd(int fd)
{
  struct file *file = NULL;
  struct thread *cur_thread = thread_current();
  struct list_elem *e = NULL;

  for (e = list_begin(&cur_thread->opened_files); e != list_end(&cur_thread->opened_files); e = e->next)
  {
    struct thread_file *th_f = list_entry(e, struct thread_file, file_elem);
    if (fd == th_f->fd)
    {
      file = th_f->file;
      break;
    }
    if (e == NULL)
      break;
  }
  return file;
}

void test_addr(const void *addr)
{
  /* 判断传入的地址是否是用户可用的虚拟地址（地址是否在PHYS_BASE以下） */
  if (!is_user_vaddr(addr))
  {
    thread_current()->exit_code = -1;
    thread_exit();
  }

  /* 判断传入的用户虚拟地址是否映射到了内核虚拟地址 */
  void *ptr = pagedir_get_page(thread_current()->pagedir, addr);
  if (!ptr)
  {
    thread_current()->exit_code = -1;
    thread_exit();
  }

  /* 每个参数地址是4byte，可能一个参数地址一半跨越界限，一半不跨越的情况 */
}

static void
syscall_handler(struct intr_frame *f)
{
  //printf("456\n");
  /*
  printf ("system call!\n");
  thread_exit ();
  */

  uint32_t *p = f->esp;
  /* 检查第一个参数所在地址是否有效 */
  test_addr(p + 1);

  /* 系統调用exit() */
  if (*p == SYS_EXIT)
  {
    /* 把退出码赋给当前线程 */
    int ecode = *(p + 1);
    thread_current()->exit_code = ecode;

    thread_exit();
  }

  /* 系統调用write() */
  else if (*p == SYS_WRITE)
  {
    int fd = *(p + 1);
    char *buf = (char *)*(p + 2);
    size_t size = *(p + 3);

    if (fd < 0 || fd == 0 || buf == NULL || buf > PHYS_BASE)
    {
      // fd或buf不合法时
      return -1;
    }

    /* fd = 1时标准输出 */
    if (fd == 1)
    {
      int total_len = 0;
      while (true)
      {
        //一次最多将WRITE_TO_CONSOLE_MAX大小的数据写入缓冲区
        if (size > MAX_CHAR_NUM)
        {
          putbuf(buf, MAX_CHAR_NUM);
          buf += MAX_CHAR_NUM;
          total_len += MAX_CHAR_NUM;
          size -= MAX_CHAR_NUM;
        }
        else
        {
          putbuf(buf, size);
          total_len += size;
          break;
        }
      }
      f->eax = total_len;
    }
    else
    {
      //输出到文件
      struct file *file = get_file_through_fd(fd);
      if (file == NULL)
      {
        f->eax = 0;
      }
      else
      {
        f->eax = file_write(file, buf, size);
      }
    }
  }
  else if (*p == SYS_READ)
  {
    int fd = *(p + 1);
    void *buf = (void *)*(p + 2);
    size_t size = *(p + 3);
    f->eax = file_read_handler(fd, buf, size);
  }
  else if (*p == SYS_CREATE)
  {
    char *name = (char *)*(p + 1);
    size_t size = *(p + 2);
    f->eax = file_create_handler(name, size);
  }

  /* 系統调用open() */
  else if (*p == SYS_OPEN)
  {
    char *name = *(p + 1);

    /* 如果文件名是NULL就exit(-1) */
    if (name == NULL)
    {
      thread_current()->exit_code = -1;
      thread_exit();
    }

    /* 用filesys_open()打开文件 */
    struct file *file = filesys_open(name);

    /* 打不开则返回-1 */
    if (file == NULL)
    {
      f->eax = -1;
      return;
    }

    /* 新建当前线程文件 */
    struct thread_file *new_file = (struct thread_file *)malloc(sizeof(struct thread_file));

    /* 设置文件fd和file地址 */
    new_file->fd = ++(thread_current()->used_fd);
    new_file->file = file;

    /* 放进当前线程文件列表中 */
    list_push_back(&(thread_current()->opened_files), &new_file->file_elem);

    /* 返回文件fd */
    f->eax = new_file->fd;
  }

  /* 系統调用close() */
  else if (*p == SYS_CLOSE)
  {
    int fd = *(p + 1);

    /* 要关闭的文件 */
    struct thread_file *close_file = NULL;

    /* 遍历当前线程打开的文件，查找fd匹配的文件 */
    struct list_elem *e;
    for (e = list_begin(&thread_current()->opened_files); e != list_end(&thread_current()->opened_files); e = list_next(e))
    {
      struct thread_file *temp = list_entry(e, struct thread_file, file_elem);
      if (fd == temp->fd)
      {
        close_file = temp;
        break;
      }
    }

    /* 如果要关闭的fd不存在则exit(-1) */
    if (close_file == NULL)
    {
      thread_current()->exit_code = -1;
      thread_exit();
    }

    /* 关闭文件 */
    file_close(close_file->file);

    /* 从当前线程文件列表移除 */
    list_remove(&close_file->file_elem);

    /* 释放内存 */
    free(close_file);
  }

  /* 系統调用exec() */
  else if (*p == SYS_EXEC)
  {
    char *name = *(p + 1);

    f->eax = process_execute(name);
  }

  /* 系統调用wait() */
  else if (*p == SYS_WAIT)
  {
    //printf("123\n");
    int pid = *(p + 1);

    f->eax = process_wait(pid);
    //printf("%d\n", f->eax);
  }

  /* 系統调用filesize() */
  else if (*p == SYS_FILESIZE)
  {
    int fd = *(p + 1);

    /* 要查找的文件 */
    struct thread_file *size_file = NULL;

    /* 遍历当前线程打开的文件，查找fd匹配的文件 */
    struct list_elem *e;
    for (e = list_begin(&thread_current()->opened_files); e != list_end(&thread_current()->opened_files); e = list_next(e))
    {
      struct thread_file *temp = list_entry(e, struct thread_file, file_elem);
      if (fd == temp->fd)
      {
        size_file = temp;
        break;
      }
    }

    /* 调用file_length计算文件大小 */
    if (size_file == NULL)
      f->eax = -1;
    else
      f->eax = file_length(size_file->file);
  }

  /* 系統调用seek() */
  else if (*p == SYS_SEEK)
  {
    int fd = *(p + 1);
    unsigned pos = *(p + 2);

    /* 要查找的文件 */
    struct thread_file *seek_file = NULL;

    /* 遍历当前线程打开的文件，查找fd匹配的文件 */
    struct list_elem *e;
    for (e = list_begin(&thread_current()->opened_files); e != list_end(&thread_current()->opened_files); e = list_next(e))
    {
      struct thread_file *temp = list_entry(e, struct thread_file, file_elem);
      if (fd == temp->fd)
      {
        seek_file = temp;
        break;
      }
    }

    /* 调用file_seek查找文件 */
    if (seek_file)
      file_seek(seek_file->file, pos);
  }
}
static bool
file_create_handler(char *name, unsigned size)
{
  bool success = false;
  if (strlen(name) > 14 || name == NULL) //FILENAME_MAX_LENGTH
    return success;

  int status = filesys_create(name, size);
  return status;
}
static int
file_read_handler(int fd, void *buffer, unsigned size)
{

  if (fd < 0 || fd == 1 || buffer == NULL)
    return -1;

  if (buffer > PHYS_BASE)
  {
    enum intr_level old_level = intr_disable();
    thread_current()->exit_code = -1;
    intr_set_level(old_level);
    thread_exit();
  }

  if (fd == 0)
  {
  }
  else
  {
    struct file *f = get_file_through_fd(fd);
    if (f == NULL)
      return -1;
    int read_bytes = file_read(f, buffer, size);
    return read_bytes;
  }

  return 0;
}